# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Subversion command abstraction.

This module exec's/call's the svn client commands (the sort of commands an
svn client wrapper might like to call).

This module simply handles the various svn operations by using Python's
subprocess module. This results in several "fork && exec"'s.

In the future this interface should be replace by lovingly hand-crafted
calls to the Python svn swig bindings.
"""


import inspect
import os
import subprocess
import sys
import types
import xml.dom.minidom

import gvn.errors
import gvn.platform


_do_command_debug = False
def EnableCommandDebug():
  global _do_command_debug
  _do_command_debug = True

def DoCommandDebug():
  return _do_command_debug


# XXX Username belongs in a context baton, not per-process.
_effective_username = None
def SetEffectiveUsername(username):
  global _effective_username
  _effective_username = username

def EffectiveUsername():
  return _effective_username

# TODO(epg): These commands don't take auth options such as
# --username; this is a stupid way to pass --username to all commands
# but these.  Do we really need to be passing that all the time
# anyway?  Seems to me the user should just pass --username himself,
# same as with svn.
_no_auth = [
  'add',
  'changelist',
  'cleanup',
  'help',
  'resolved',
  'revert',
]


def quote(s):
  return ''.join(["'", s.replace("'", "'\''"), "'"])

def _ProcessArgv(argv):
  global _no_auth
  argv = argv

  if ((len(argv) >= 2
       and os.path.basename(argv[0]) == 'svn'
       and argv[1] not in _no_auth)
      and EffectiveUsername()):
    argv.insert(1, EffectiveUsername())
    argv.insert(1, '--username')

  if DoCommandDebug():
    sys.stderr.write(' '.join(quote(x) for x in argv))
    sys.stderr.write('\n')
#     for (_, filename, line, function, lines, _) in inspect.stack():
#         sys.stderr.write(' File "%s", line %d, in %s\n'
#                          % (filename, line, function))
#         for line in lines:
#           sys.stderr.write(line)
  return argv

def Start(argv):
  return subprocess.Popen(_ProcessArgv(argv),
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)

def Capture(argv):
  proc = Start(argv)
  result = list(proc.communicate())
  result.insert(0, proc.returncode)
  return result

def Run(argv, stdout_callback=None, stderr_callback=None):
  if stdout_callback is None and stderr_callback is None:
    # Make sure anything *we* have printed goes out before we let svn
    # start printing things.
    sys.stdout.flush()
    sys.stderr.flush()
    return subprocess.call(_ProcessArgv(argv))

  raise gvn.errors.Internal('callbacks not yet supported')

  # Start threads like Popen.communicate does, but call the callback
  # with a line each time we get one from the corresponding output.
  # This is so 'gvn sync' can print update lines as they come in, not
  # buffer all 7 million of them sitting there printing nothing for 20
  # minutes, then spewing them all at the end (*cough* gcheckout).

  return proc.retcode

def _Call(f, cmd, argv=None, options=None):
  if argv is None:
    argv = [cmd]
  else:
    argv.insert(0, cmd)

  if options is not None:
    # TODO(epg): these ain't all...
    if options.no_auth_cache is not None:
      argv.insert(1, '--no-auth-cache')
    if options.username is not None:
      argv.insert(1, options.username)
      argv.insert(1, '--username')
    if options.password is not None:
      argv.insert(1, options.password)
      argv.insert(1, '--password')

  return f(argv)

def CaptureSvn(argv=None):
  return _Call(Capture, gvn.SVN, argv)

def CaptureSvnAdmin(argv=None):
  return _Call(Capture, gvn.SVNADMIN, argv)

def RunSvn(argv=None, options=None):
  return _Call(Run, gvn.SVN, argv, options)

def RunSvnAdmin(argv=None):
  return _Call(Run, gvn.SVNADMIN, argv)

def StartSvn(argv=None):
  return _Call(Start, gvn.SVN, argv)

def StartSvnAdmin(argv=None):
  return _Call(Start, gvn.SVNADMIN, argv)
